/**
   @file modes.c
   @brief Function definitions that provide functionality for modes in the generator.
 */
#include <stdlib.h>
#include "cJSON.h"
#include "resource_model.h"
#include "random.h"

/** Translate each mode's probability of occuring into a threshold point between 0 and 1. 
    Example: 
    Mode 1 p = .5
    Mode 2 p = .2
    Mode 3 p = .3
    
    After running setup_modes_probability, the adjusted thresholds will be
    
    Mode 1 p = .5
    Mode 2 p = .7
    Mode 3 p = 1
    
    This allows select_mode to choose a mode using a random number between 0 and 1.
*/
void setup_modes_probability(mode **, int);

/**
   @param table A hash table unique to a resource to store all of its supported modes.
   @param key The identifying key for the to be defined mode.
   @param construct_from_json a function to translate a JSON object into a mode.
 */
void install_mode(mode_definition_list *table[HASHSIZE], char *key, mode *(*construct_from_json)(cJSON *)) 
{
  hashed target = hash(key);
  mode_definition_list *list = malloc(sizeof(mode_definition_list));
  list->key = key;
  list->build_mode = construct_from_json;
  list->next = table[target];
  table[target] = list;
}


mode *setup_mode(mode_definition_list *table[HASHSIZE], cJSON *json)
{
  cJSON *json_name = cJSON_GetObjectItem(json, "name");
  char *name = json_name->valuestring;
  hashed target = hash(name);
  mode_definition_list *list = table[target];
  for(list; list != NULL;list = list->next)
    {
      if(strcmp(name, list->key) == 0) 
        {
          return list->build_mode(json);
        }
    }
  return NULL;
}


mode **initialize_modes(mode_definition_list *table[HASHSIZE], cJSON *json) {
  cJSON *json_modes = cJSON_GetObjectItem(json, "modes");
  int cnt_modes = cJSON_GetArraySize(json_modes);
  mode **modes = malloc(sizeof(mode *)*cnt_modes);
  int i = 0;
  for(i; i < cnt_modes; i++)
    {
      cJSON *json_mode = cJSON_GetArrayItem(json_modes, i);
      modes[i] = setup_mode(table, json_mode);
    }
  if(modes_valid(modes, cnt_modes) == 0 ) {
    return NULL;
  }
  setup_modes_probability(modes, cnt_modes);
  return modes;
}

/** Check if a set of modes are valid. Their unadjusted probabilities will add to 1 if they are valid. */
int modes_valid(mode **modes, int size) {
  double total = 0;
  int i = 0;
  for(i; i < size; i++) {
    total += modes[i]->probability;
  }
  return ((int)total == 1) ? 1 : 0;
}

void setup_modes_probability(mode **modes, int size) {
  double total = 0;
  int i = 0;
  for(i; i < size; i++) {
    total += modes[i]->probability;
    modes[i]->probability = total;
  }
}

mode *select_mode(resource_model *model){
  double selection = get_random_number("wg_rand");
  int i = 0;
  for(i; i < model->cnt_modes; i++) {
    if(model->modes[i]->probability >= selection)
      {
        return model->modes[i];
      }
  }
}

/**
 * Count the number of modes in a json object
 */
int count_modes(cJSON *json) {
  cJSON *json_modes = cJSON_GetObjectItem(json, "modes");
  return cJSON_GetArraySize(json_modes);
}
